/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.scriptmark.core;

import com.jspx.boot.environment.Environment;
import com.jspx.io.AbstractRead;
import com.jspx.io.AutoReadTextFile;
import com.jspx.scriptmark.*;
import com.jspx.scriptmark.core.block.*;
import com.jspx.scriptmark.core.script.TemplateScriptEngine;
import com.jspx.scriptmark.core.script.ScriptTypeConverter;
import com.jspx.scriptmark.core.dispose.*;
import com.jspx.scriptmark.exception.ScriptException;
import com.jspx.scriptmark.exception.ScriptRunException;
import com.jspx.utils.FileUtil;
import com.jspx.utils.StringUtil;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.io.*;
import java.util.logging.Level;

/**
 * Created by IntelliJ IDEA.
 * User: chenYuan
 * Date: 2008-11-16
 * Time: 15:33:04
 */
public class EnvRunner {
    static private java.util.logging.Logger log = java.util.logging.Logger.getLogger(EnvRunner.class.getName());
    private TemplateModel template = null;
    private ScriptRunner scriptEngine = null;
    static private String variableBegin = "${";
    static private String variableEnd = "}";
    static private char escapeVariable = '\\';
    private String currentPath = FileUtil.mendPath(System.getProperty("user.dir")); //当前路径，f方便include 使用
    private String rootDirectory = FileUtil.mendPath(System.getProperty("user.dir")); //路径范围
    static private Map<String, Phrase> phrases = new HashMap<String, Phrase>();

    static
    {
        phrases.put(HtmlEngineImpl.NoneTag, new NonePhrase());
        phrases.put(CommentBlock.class.getName(), new CommentPhrase());
        phrases.put(CompressBlock.class.getName(), new CompressPhrase());
        phrases.put(AssignBlock.class.getName(), new AssignPhrase());
        phrases.put(CallBlock.class.getName(), new CallPhrase());
        phrases.put(IfBlock.class.getName(), new IfPhrase());
        phrases.put(IncludeBlock.class.getName(), new IncludePhrase());
        phrases.put(ListBlock.class.getName(), new ListPhrase());
        phrases.put(ScriptBlock.class.getName(), new ScriptPhrase());
        phrases.put(SettingBlock.class.getName(), new SettingPhrase());
        phrases.put(SwitchBlock.class.getName(), new SwitchPhrase());
        phrases.put(TryBlock.class.getName(), new TryPhrase());
        phrases.put(ElseBlock.class.getName(), new ElsePhrase());
        phrases.put(CaseBlock.class.getName(), new ElsePhrase());
        phrases.put(DefaultBlock.class.getName(), new ElsePhrase());
        phrases.put(MacroBlock.class.getName(), new MacroPhrase());
    }

    /**
     * @param template 初始
     */
    public EnvRunner(TemplateModel template) {
        this.template = template;

        /////////配置初始begin
        Configurable config = this.template.getConfigurable();
        variableBegin = config.getString(ScriptmarkEnv.VariableBegin);
        variableEnd = config.getString(ScriptmarkEnv.VariableEnd);
        escapeVariable = config.getString(ScriptmarkEnv.escapeVariable).charAt(0);
        /////////配置初始end                                                                                                                x
    }


    /**
     * @return 得到脚本运行环境
     */
    public ScriptRunner getScriptRunner() {
        //安卓只能使用单例的脚本引擎，否则会错误
        if (scriptEngine == null || scriptEngine.isClosed())
        {
            scriptEngine = new TemplateScriptEngine();
        }
        return scriptEngine;
    }

    /**
     * @return 得到模板
     */
    public TemplateModel getTemplate() {
        return template;
    }


    /**
     * 运行输出
     *
     * @param out 输出
     * @throws IOException            io错误
     * @throws ClassNotFoundException 没有class
     * @throws IllegalAccessException 线程错误
     * @throws InstantiationException 异常
     * @throws Exception        脚本错误
     */
    void Runner(Writer out) throws Exception {
        List<TagNode> importList = getAutoImport(template.getConfigurable());
        if (importList != null && !importList.isEmpty()) {
            Writer writer = new StringWriter();
            for (TagNode tagNode : importList) {
                runBlock(tagNode, writer);
            }
        }
        //自动包含的部分
        //out.write(autoInlucde());
        //向下执行
        List<TagNode> rootList = template.getRootTree();
        for (TagNode tagNode : rootList) {
            try {
                int b = runBlock(tagNode, out);
                if (b < 0) break;
            } catch (Exception e) {
                if (e instanceof ScriptRunException) {
                    ScriptRunException ex = (ScriptRunException) e;
                    if (ex.getTagNode() != null) {
                        String source = template.getSource();
                        String tagSource = ex.getTagNode().getSource();
                        int i = source.indexOf(tagSource);
                        if (i == -1) {
                            i = source.length();
                        } else
                            i = source.substring(0, i).length();

                        String str = source.substring(0, i);

                        int errorLine = (1 + StringUtil.countMatches(str, StringUtil.CR) + ex.getTagNode().getErrorLine());


                        log.log(Level.FINE,errorLine + " " +source,ex);
                        throw new ScriptException(ex.getMessage(), tagNode.getTagName(), errorLine, tagNode.getStarLength());
                    }
                    log.log(Level.FINE,tagNode.getTagName() + " " +tagNode.getLineNumber()   + "  " + tagNode.getSource(),ex);
                    throw new ScriptException(ex.getMessage(), tagNode.getTagName(), tagNode.getLineNumber(), tagNode.getStarLength());
                }
                log.log(Level.FINE,tagNode.getTagName() + " " +tagNode.getLineNumber() + "  " + tagNode.getSource(),e);
                throw new ScriptException(e.getMessage(), tagNode.getTagName(), tagNode.getLineNumber(), tagNode.getStarLength());
            }
        }
    }

    /**
     * @return 当前路径
     */
    public String getCurrentPath() {
        return currentPath;
    }

    /**
     * @param currentPath 当前路径
     */
    public void setCurrentPath(String currentPath) {
        this.currentPath = currentPath;
    }

    /**
     * @return 根路径
     */
    public String getRootDirectory() {
        return rootDirectory;
    }

    /**
     * @param rootDirectory 根路径
     */
    public void setRootDirectory(String rootDirectory) {
        this.rootDirectory = rootDirectory;
    }

    /**
     * @param tagNode 节点
     * @param out     输出
     * @throws ScriptException 脚本错误
     * @throws IOException     读取错误
     */
    public void getInjectVariables(TagNode tagNode, Writer out) throws Exception {
        ScriptTypeConverter.getInjectVariables(scriptEngine, tagNode, out, variableBegin, variableEnd, escapeVariable);
    }

    /**
     * 运行一个代码块,运行后的结果 保存在Writer中
     *
     * @param tagNode 块节点
     * @param out     输出
     * @throws ScriptException        脚本错误
     * @throws IOException            读取错误
     * @throws ClassNotFoundException 类错误
     * @throws IllegalAccessException IllegalAccessException
     * @throws InstantiationException InstantiationException
     */
    public int runBlock(TagNode tagNode, Writer out) throws Exception {
        if (tagNode == null) return 0;
        if (tagNode.getTagName().equals(BreakBlock.name)) return BreakBlock.value;
        if (tagNode.getTagName().equals(ContinueBlock.name)) return ContinueBlock.value;
        Phrase phrase = phrases.get(tagNode.getClass().getName());
        if (phrase == null) phrase = phrases.get(HtmlEngineImpl.NoneTag);
        return phrase.getRun(this, tagNode, out);
    }

    /**
     * @param configurable 配置
     * @return 得到配置的节点 默认都是宏
     * @throws ScriptException        脚本错误
     * @throws IOException            读取错误
     * @throws ClassNotFoundException 没有类
     * @throws IllegalAccessException 执行错误
     * @throws InstantiationException 验证性错误
     */
    static private List<TagNode> getAutoImport(Configurable configurable) throws Exception {
        String[] paths = configurable.getSearchPath();
        if (paths == null) return null;
        TemplateLoader templateLoader = TemplateManager.getInstance();
        StringBuilder autoImportSrc = new StringBuilder();
        autoImportSrc.append(templateLoader.get(ScriptmarkEnv.autoImportCache));
        if (autoImportSrc.length()<5)
        {
            for (String importFile : configurable.getAutoImports()) {
                if (StringUtil.isNULL(importFile)) continue;
                for (String path : paths) {
                    File f = new File(path, importFile);
                    if (FileUtil.isFileExist(f.getAbsolutePath()))
                    {
                        AbstractRead read = new AutoReadTextFile();
                        read.setEncode(Environment.defaultEncode);
                        read.setFile(f);
                        autoImportSrc.append(read.getContent()).append(StringUtil.CRLF);
                    }
                }
            }
        }
        TemplateElement templateEl = new TemplateElement(autoImportSrc.toString(),0, configurable);
        return templateEl.getRootTree();
    }
}